package goredis

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"gitee.com/MikeDDD/gu/logs"
	"github.com/go-redis/redis/v8"
	"github.com/golang/protobuf/proto"
)

func ExpireCli(cli *RedisClienterInstance, key any, t time.Duration) (err error) {
	cmd := cli.Rc.Expire(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), t)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoExpire error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func PersistCli(cli *RedisClienterInstance, key any) (err error) {
	cmd := cli.Rc.Persist(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoPersist error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func TTLCli(cli *RedisClienterInstance, key any) (s time.Duration, err error) {
	cmd := cli.Rc.TTL(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoTTL error! key=%v, err=%v", key, cmd.Err())
		return -1, cmd.Err()
	}

	return cmd.Val(), nil
}

func KeysCli(cli *RedisClienterInstance, key any) (field []string, err error) {
	res := cli.Rc.Keys(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do KEYS error! key=%v, err=%v", key, res.Err())
		return field, res.Err()
	}

	for _, v := range res.Val() {
		field = append(field, strings.Replace(v, fmt.Sprintf("%v:", cli.serverid), "", -1))
	}

	return field, nil
}

func DelCli(cli *RedisClienterInstance, keys ...string) error {
	var tempKey []string
	for _, key := range keys {
		key = fmt.Sprintf("%v:%v", cli.serverid, key)
		tempKey = append(tempKey, key)
	}
	res := cli.Rc.Del(cli.ctx(), tempKey...)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Del fail", res.Err())
		return res.Err()
	}

	return nil
}

func ExistsCli(cli *RedisClienterInstance, key any) (bool, error) {
	res := cli.Rc.Exists(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do EXISTS error! key=%v, err=%v", key, res.Err())
		return false, res.Err()
	}

	return res.Val() > 0, nil
}

func SetExtCli(cli *RedisClienterInstance, key any, p any) (err error) {
	bytes, err := proto.Marshal(p.(proto.Message))
	if err != nil {
		logs.Error("Protobuf Marshal error:%v", err)
		return err
	}

	res := cli.Rc.Set(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), bytes, time.Hour*2)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do HEXISTS error! key=%v, err=%v", key, res.Err())
		return res.Err()
	}

	return nil
}

// -1 永不过期
func SetCli(cli *RedisClienterInstance, key, value string, ttl time.Duration) error {
	if res := cli.Rc.Set(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), value, ttl); res.Err() != nil &&
		!errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis set error! %v", res.Err())
		return res.Err()
	}
	return nil
}

/*
String CompareAndSwap 带 ttl

当 key 不存在时：
	写入 newValue 返回 true

当 key 存在时：
	当 value==oldValue 时，写入 newValue 返回 true
	当 value!=oldValue 时，返回 false
*/
func SetCliCas(cli *RedisClienterInstance, key, oldValue, newValue string, second int64) (bool, error) {
	res, err := cli.Rc.Eval(
		cli.ctx(),
		`
			local old=redis.call('get', KEYS[1])

			if not old or old==ARGV[1] then
				if redis.call('setex', KEYS[1], ARGV[3], ARGV[2]) then
					return 1
				else
					return 0
				end
			else
				return 0
			end
		`,
		[]string{fmt.Sprintf("%v:%v", cli.serverid, key)},
		[]any{oldValue, newValue, second},
	).Result()

	if err != nil && !errors.Is(err, redis.Nil) {
		return false, err
	}

	if res.(int64) == 1 {
		return true, nil
	} else {
		return false, nil
	}
}

func EvalCli(cli *RedisClienterInstance, script string, key string, args []any) (any, bool, error) {
	res, err := cli.Rc.Eval(
		cli.ctx(),
		script,
		[]string{fmt.Sprintf("%v:%v", cli.serverid, key)},
		args,
	).Result()

	if err != nil && !errors.Is(err, redis.Nil) {
		return res, false, err
	}

	return res, true, nil
}

func GetCli(cli *RedisClienterInstance, key string) (string, error) {
	res := cli.Rc.Get(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))

	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis set error! %v", res.Err())
		return "", res.Err()
	}

	return res.Val(), nil
}

func GetExtCli(cli *RedisClienterInstance, key any, p any) (exists bool, err error) {
	res := cli.Rc.Get(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do GetExt error! key=%v, err=%v", key, res.Err())
		return false, res.Err()
	}

	if len(res.Val()) < 1 {
		return false, nil
	}

	err = proto.Unmarshal([]byte(res.Val()), p.(proto.Message))
	if err != nil {
		logs.Error("Redis Do GET Protobuf Unmarshal error! key=%v, err=%v", key, err)
		return false, err
	}
	return true, nil
}

func HGetCli(cli *RedisClienterInstance, key any, field any) string {
	res := cli.Rc.HGet(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", field))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("HGet fail", res.Err())
		return ""
	}

	return res.Val()
}

func HGetExtCli(cli *RedisClienterInstance, key any, field any, p any) (exists bool, ok bool) {
	res := cli.Rc.HGet(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", field))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("DoHGetExt fail:", res.Err())
		return false, false
	}

	if len(res.Val()) < 1 {
		return false, true
	}

	err := proto.Unmarshal([]byte(res.Val()), p.(proto.Message))
	if err != nil {
		logs.Error("Redis Do HGET Protobuf Unmarshal error! key=%v, field=%v, err=%v", key, field, err)
		return false, false
	}

	return true, true
}

func HSetCli(cli *RedisClienterInstance, key any, field any, p any) bool {
	_, err := cli.Rc.HSet(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", field), fmt.Sprintf("%v", p)).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		logs.Error("HSet fail", err, p)
		return false
	}
	return true
}

func HSetExtCli(cli *RedisClienterInstance, key any, field any, p any) (err error) {
	bytes, err := proto.Marshal(p.(proto.Message))
	if err != nil && !errors.Is(err, redis.Nil) {
		logs.Error("Redis Do HSET Protobuf Marshal error! key=%v, field=%v, err=%v", key, field, err)
		return err
	}

	res := cli.Rc.HSet(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), field, bytes)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("DoHSetExt fail", res.Err())
		return res.Err()
	}

	return nil
}

func HValsCli(cli *RedisClienterInstance, key any) (p []string, err error) {
	res := cli.Rc.HVals(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error(res.Err())
		return nil, res.Err()
	}

	return res.Val(), nil
}

func HValsExtCli(cli *RedisClienterInstance, key any, p any) (err error) {
	res := cli.Rc.HVals(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error(res.Err())
		return res.Err()
	}

	vs := res.Val()

	results := reflect.ValueOf(p)
	if results.Kind() == reflect.Ptr {
		results = results.Elem()
	}

	for _, v := range vs {
		newItem := reflect.New(getStructType(p)).Interface()
		err = proto.Unmarshal([]byte(v), newItem.(proto.Message))
		if err != nil {
			logs.Error("Redis Do HVALS Protobuf Unmarshal error! key=%v, err=%v", key, err)
			return
		}
		results.Set(reflect.Append(results, reflect.ValueOf(newItem)))
	}

	return nil
}

func HLenCli(cli *RedisClienterInstance, key any) (int64, error) {
	res := cli.Rc.HLen(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error(res.Err())
		return 0, res.Err()
	}
	return res.Val(), nil
}

func HDelCli(cli *RedisClienterInstance, key string, fields ...string) error {
	if len(fields) == 0 {
		return nil
	}

	res := cli.Rc.HDel(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fields...)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("HDel fail", res.Err())
		return res.Err()
	}

	return nil
}

func HExistsCli(cli *RedisClienterInstance, key any, field any) (bool, error) {
	res := cli.Rc.HExists(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", field))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do HEXISTS error! key=%v, err=%v", key, res.Err())
		return false, res.Err()
	}

	return res.Val(), nil
}

func HKeysCli(cli *RedisClienterInstance, key any) (field []string, err error) {
	res := cli.Rc.HKeys(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("Redis Do HKEYS error! key=%v, err=%v", key, res.Err())
		return field, res.Err()
	}

	return res.Val(), nil
}

func HIncrByCli(cli *RedisClienterInstance, rk, hk string, hv int64) int64 {
	res := cli.Rc.HIncrBy(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, rk), hk, hv)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("HIncrBy fail", res.Err())
		return -1
	}
	return res.Val()
}

// ======== zset ========

func ZAddCli(cli *RedisClienterInstance, key string, score float64, member string) (err error) {
	res := cli.Rc.ZAdd(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), &redis.Z{
		Member: member,
		Score:  score,
	})

	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("ZAdd fail", res.Err())
		return res.Err()
	}

	return nil
}

func ZIncrByCli(cli *RedisClienterInstance, key string, score float64, member string) (err error) {
	res := cli.Rc.ZIncrBy(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), score, member)
	if res.Err() != nil && !errors.Is(res.Err(), redis.Nil) {
		logs.Error("ZIncr fail", res.Err())
		return res.Err()
	}

	return nil
}

func ZRevRangeWithScoresCli(cli *RedisClienterInstance, key string, start, stop int64) (err error, memberResult []string, scoreResult []float64) {
	cmd := cli.Rc.ZRevRangeWithScores(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRevRangeWithScores error! key=%v, start=%v, stop=%v, err=%v", key, start, stop, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		memberResult = append(memberResult, val.Member.(string))
		scoreResult = append(scoreResult, val.Score)
	}

	return
}

func ZRevRangeByScoreWithScoresCli(cli *RedisClienterInstance, key string, min, max string, offset, count int64) (err error, memberResult []string, scoreResult []float64) {
	cmd := cli.Rc.ZRevRangeByScoreWithScores(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), &redis.ZRangeBy{
		Min:    min,
		Max:    max,
		Offset: offset,
		Count:  count,
	})

	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRevRangeWithScores error! key=%v, start=%v, stop=%v, %v %v err=%v", key, min, max, offset, count, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		memberResult = append(memberResult, val.Member.(string))
		scoreResult = append(scoreResult, val.Score)
	}

	return
}

func ZRangeByScoreWithScoresCli(cli *RedisClienterInstance, key string, min, max string, offset, count int64) (err error, memberResult []string, scoreResult []float64) {
	cmd := cli.Rc.ZRangeByScoreWithScores(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), &redis.ZRangeBy{
		Min:    min,
		Max:    max,
		Offset: offset,
		Count:  count,
	})

	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRevRangeWithScores error! key=%v, start=%v, stop=%v, %v %v err=%v", key, min, max, offset, count, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		memberResult = append(memberResult, val.Member.(string))
		scoreResult = append(scoreResult, val.Score)
	}

	return
}

func ZRangeByScoreCli(cli *RedisClienterInstance, key string, min, max string, offset, count int64) (err error, memberResult []string) {
	cmd := cli.Rc.ZRangeByScore(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), &redis.ZRangeBy{
		Min:    min,
		Max:    max,
		Offset: offset,
		Count:  count,
	})

	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRevRangeWithScores error! key=%v, start=%v, stop=%v, %v %v err=%v", key, min, max, offset, count, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		memberResult = append(memberResult, val)
	}

	return
}

func ZRevRangeCli(cli *RedisClienterInstance, key string, start, stop int64) (err error, memberResult []string) {
	cmd := cli.Rc.ZRevRange(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRevRangeWithScores error! key=%v, start=%v, stop=%v, err=%v", key, start, stop, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		memberResult = append(memberResult, val)
	}

	return
}

func ZRangeWithScoresCli(cli *RedisClienterInstance, key string, start, stop int64) (err error, memberResult []string, scoreResult []float64) {
	cmd := cli.Rc.ZRangeWithScores(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRangeWithScores error! key=%v, start=%v, stop=%v, err=%v", key, start, stop, cmd.Err())
		return
	}

	for _, val := range cmd.Val() {
		scoreResult = append(scoreResult, val.Score)
		memberResult = append(memberResult, val.Member.(string))
	}

	return
}

func ZRemCli(cli *RedisClienterInstance, key string, member ...string) error {
	cmd := cli.Rc.ZRem(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), member)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoZRem error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func ZRevRankCli(cli *RedisClienterInstance, key string, member string) (rank int64, err error) {
	cmd := cli.Rc.ZRevRank(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", member))
	if cmd.Err() == nil {
		return cmd.Val(), nil
	}
	if errors.Is(cmd.Err(), redis.Nil) {
		return -1, nil
	}

	return 0, cmd.Err()
}

func ZScoreCli(cli *RedisClienterInstance, key string, member string) (score float64, err error) {
	cmd := cli.Rc.ZScore(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", member))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoZRevRank error! key=%v, err=%v", key, cmd.Err())
		return 0, cmd.Err()
	}

	return cmd.Val(), err
}

func ZCardCli(cli *RedisClienterInstance, key string) (cnt int64, err error) {
	cmd := cli.Rc.ZCard(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoZRevRank error! key=%v, err=%v", key, cmd.Err())
		return 0, cmd.Err()
	}

	return int64(cmd.Val()), err
}

func ZRankCli(cli *RedisClienterInstance, key string, member string) (rank int64, err error) {
	cmd := cli.Rc.ZRank(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), fmt.Sprintf("%v", member))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis ZRANK error! key=%v, err=%v", key, cmd.Err())
		return 0, cmd.Err()
	}

	return cmd.Val(), err
}

// ======== zset ========

func LPushCli(cli *RedisClienterInstance, key string, val string) (err error) {
	cmd := cli.Rc.LPush(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), val)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis LPushCli error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func LPushExtCli(cli *RedisClienterInstance, key any, p any) (err error) {
	bytes, err := proto.Marshal(p.(proto.Message))
	if err != nil {
		logs.Error("Redis DoLPush Protobuf Marshal error! key=%v, err=%v", key, err)
		return err
	}

	cmd := cli.Rc.LPush(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), bytes)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoLPush error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func RPushExtCli(cli *RedisClienterInstance, key any, p any) (err error) {
	bytes, err := proto.Marshal(p.(proto.Message))
	if err != nil {
		logs.Error("Redis DoLPush Protobuf Marshal error! key=%v, err=%v", key, err)
		return err
	}

	cmd := cli.Rc.RPush(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), bytes)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoLPush error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func RPushCli(cli *RedisClienterInstance, key string, value string) (err error) {
	cmd := cli.Rc.RPush(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), value)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoLPush error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func LTrimCli(cli *RedisClienterInstance, key string, start, stop int64) error {
	cmd := cli.Rc.LTrim(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis LTrimCli error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	return nil
}

func LRangeCli(cli *RedisClienterInstance, key string, start, stop int64) (error, []string) {
	cmd := cli.Rc.LRange(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis LRangeCli error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err(), nil
	}

	return nil, cmd.Val()
}

func LRangeExtCli(cli *RedisClienterInstance, key any, slicePrt any, start, stop int64) (err error) {
	cmd := cli.Rc.LRange(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key), start, stop)
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoLPush error! key=%v, err=%v", key, cmd.Err())
		return cmd.Err()
	}

	results := reflect.ValueOf(slicePrt)
	if results.Kind() == reflect.Ptr {
		results = results.Elem()
	}

	for _, v := range cmd.Val() {
		newItem := reflect.New(getStructType(slicePrt)).Interface()
		err = proto.Unmarshal([]byte(v), newItem.(proto.Message))
		if err != nil {
			logs.Error("Redis DoLRangeExt Protobuf Unmarshal error! key=%v, err=%v", key, err)
			return
		}
		results.Set(reflect.Append(results, reflect.ValueOf(newItem)))

	}

	return nil
}

func LLenCli(cli *RedisClienterInstance, key any) (int64, error) {
	cmd := cli.Rc.LLen(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoLLen error! key=%v, err=%v", key, cmd.Err())
		return 0, cmd.Err()
	}

	return cmd.Val(), nil
}

func RPopCli(cli *RedisClienterInstance, key any) (value string, err error) {
	cmd := cli.Rc.RPop(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoRPop error! key=%v, err=%v", key, cmd.Err())
		return "", cmd.Err()
	}

	return cmd.Val(), err
}

func LPopCli(cli *RedisClienterInstance, key any) (value string, err error) {
	cmd := cli.Rc.LPop(cli.ctx(), fmt.Sprintf("%v:%v", cli.serverid, key))
	if cmd.Err() != nil && !errors.Is(cmd.Err(), redis.Nil) {
		logs.Error("Redis DoRPop error! key=%v, err=%v", key, cmd.Err())
		return "", cmd.Err()
	}

	return cmd.Val(), err
}
